/*-
 * Copyright 2012 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#ifndef __sys_h__
#define __sys_h__
//---------------------------------------------------------------------------
#include "config.h"
#include "array.h"
#include "str.h"
//---------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
//---------------------------------------------------------------------------
void microacid_system_test_and_init(void);
void microacid_system_destroy(void);
//---------------------------------------------------------------------------
static __inline__ uint8_t get_bit(void * bitmap,uintptr_t n)
{
  return ((uint8_t *) bitmap)[n >> 3] & (uint8_t) (1u << (n & 7));
}
//---------------------------------------------------------------------------
static __inline__ void set_bit(void * bitmap,uintptr_t n)
{
  ((uint8_t *) bitmap)[n >> 3] |= (uint8_t) (1u << (n & 7));
}
//---------------------------------------------------------------------------
static __inline__ void reset_bit(void * bitmap,uintptr_t n)
{
  ((uint8_t *) bitmap)[n >> 3] &= (uint8_t) ~(1u << (n & 7));
}
//---------------------------------------------------------------------------
static __inline__ void invert_bit(void * bitmap,uintptr_t n)
{
  ((uint8_t *) bitmap)[n >> 3] ^= (uint8_t) (1u << (n & 7));
}
//---------------------------------------------------------------------------
void set_bit_range(void * bitmap,uintptr_t n,uintptr_t c);
void reset_bit_range(void * bitmap,uintptr_t n,uintptr_t c);
void invert_bit_range(void * bitmap,uintptr_t n,uintptr_t c);
//---------------------------------------------------------------------------
static __inline__ void memxchg(void * m1,void * m2,uintptr_t n)
{
  while( n >= sizeof(uintptr_t) ){
    uintptr_t v = *(uintptr_t *) m1;
    *(uintptr_t *) m1 = *(uintptr_t *) m2;
    *(uintptr_t *) m2 = v;
    m1 = (uintptr_t *) m1 + 1;
    m2 = (uintptr_t *) m2 + 1;
    n -= sizeof(uintptr_t);
  }
  while( n > 0 ){
    uint8_t v = *(uint8_t *) m1;
    *(uint8_t *) m1 = *(uint8_t *) m2;
    *(uint8_t *) m2 = v;
    m1 = (uint8_t *) m1 + 1;
    m2 = (uint8_t *) m2 + 1;
    n--;
  }
}
//---------------------------------------------------------------------------
uintptr_t ge2(uintptr_t n);
uintptr_t le2(uintptr_t n);
//-----------------------------------------------------------------------------
void print_new_signature(void);
//-----------------------------------------------------------------------------
void * salloc(uintptr_t size,void * p);
void sfree(void * p);
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// path functions
//---------------------------------------------------------------------------
void extract_path(string * path,const char * file_name);
void extract_name(string * name,const char * file_name);
void extract_path_name(string * path,string * name,const char * file_name);
void dir_list_init(array * dir_list);
void dir_list_get(array * dir_list,const char * filter,bool sub_dirs,bool sort);
//---------------------------------------------------------------------------
// portable system functions
//---------------------------------------------------------------------------
#if _MSC_VER || __MINGW__  // ненавижу микрософт
typedef int64_t portable_off_t;
typedef intptr_t portable_ssize_t;
#else
typedef off_t portable_off_t;
typedef ssize_t portable_ssize_t;
#endif
//---------------------------------------------------------------------------
#if HAVE__STAT64
typedef struct _stat64 portable_stat_t;
#elif HAVE__STATI64
typedef struct _stati64 portable_stat_t;
#else
typedef struct stat portable_stat_t;
#endif
#ifndef S_IFDIR
#define S_IFDIR _S_IFDIR
#endif
//---------------------------------------------------------------------------
#if _MSC_VER  // ненавижу микрософт
typedef uint16_t mode_t;
struct dirent {
  char * d_name;
};
typedef struct DIR {
    intptr_t               handle; /* -1 for failed rewind */
    struct _finddatai64_t  info;
    struct dirent          result; /* d_name null iff first time */
    char                   * name;  /* null-terminated char string */
} DIR;
#endif
//---------------------------------------------------------------------------
DIR * portable_opendir(const char * dir_name);
struct dirent * portable_readdir(DIR * dir);
void portable_rewinddir(DIR * dir);
int portable_closedir(DIR * dir);
int portable_stat(const char * file_name,portable_stat_t * st);
int portable_mkdir(const char * dir_name,mode_t mode,bool make_sub_dirs);
portable_off_t portable_lseek(int handle,portable_off_t offset,int origin);
int portable_open(const char * file_name,int flags,mode_t mode,bool make_dirs);
int portable_close(int handle);
int portable_unlink(const char * name);
int portable_access(const char * name,int mode);
portable_ssize_t portable_read(int handle,void * buf,size_t size);
portable_ssize_t portable_write(int handle,const void * buf,size_t size);
portable_ssize_t portable_pread(int handle,void * buf,size_t size,portable_off_t offset);
portable_ssize_t portable_pwrite(int handle,const void * buf,size_t size,portable_off_t offset);
#if HAVE_READV
portable_ssize_t portable_readv(int handle,const struct iovec * iov,int iovcnt);
#endif
#if HAVE_WRITEV
portable_ssize_t portable_writev(int handle,const struct iovec * iov,int iovcnt);
#endif
#if HAVE_PREADV
portable_ssize_t portable_preadv(int handle,const struct iovec * iov,int iovcnt,portable_off_t offset);
#endif
#if HAVE_PWRITEV
portable_ssize_t portable_pwritev(int handle,const struct iovec * iov,int iovcnt,portable_off_t offset);
#endif
int portable_chsize(int handle,portable_off_t size);
portable_off_t portable_filelength(int handle);
int portable_fsync(int handle);
////---------------------------------------------------------------------------
void portable_sleep(uint64_t timeout);
void portable_sleep1(void);
uintptr_t portable_gettid(void);
uintptr_t system_cores();
//-----------------------------------------------------------------------------
// endian transformation functions
//-----------------------------------------------------------------------------
#if !HAVE_HTOBE16
//-----------------------------------------------------------------------------
static __inline__ uint16_t htobe16(uint16_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return (uint16_t) ((u >> 8) | ((u & 0xff) << 8));
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_HTOBE32
//---------------------------------------------------------------------------
static __inline__ uint32_t htobe32(uint32_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return (uint32_t) ((u >> 24) | ((u >> 8) & 0xff00) | ((u << 8) & 0xff0000) | (u << 24));
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_HTOBE64
//---------------------------------------------------------------------------
static __inline__ uint64_t htobe64(uint64_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return ((uint64_t) htobe32((uint32_t) u) << 32) | htobe32((uint32_t) (u >> 32));
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE16TOH
//---------------------------------------------------------------------------
static __inline__ uint16_t le16toh(uint16_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return (uint16_t) ((u >> 8) | ((u & 0xff) << 8));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE32TOH
//---------------------------------------------------------------------------
static __inline__ uint32_t le32toh(uint32_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return (uint32_t) ((u >> 24) | ((u >> 8) & 0xff00) | ((u << 8) & 0xff0000) | (u << 24));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE64TOH
//---------------------------------------------------------------------------
static __inline__ uint64_t le64toh(uint64_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return ((uint64_t) htobe32((uint32_t) u) << 32) | htobe32((uint32_t) (u >> 32));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE16TOH
//---------------------------------------------------------------------------
static __inline__ uint16_t be16toh(uint16_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return (uint16_t) ((u >> 8) | ((u & 0xff) << 8));
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE32TOH
//---------------------------------------------------------------------------
static __inline__ uint32_t be32toh(uint32_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return (u << 24) | ((u << 8) & 0xff0000) | ((u >> 8) & 0xff00) | (u >> 24);
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE64TOH
//---------------------------------------------------------------------------
static __inline__ uint64_t be64toh(uint64_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return ((uint64_t) be32toh((uint32_t) u) << 32) | be32toh((uint32_t) (u >> 32));
#else
  return u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_HTOLE16
//---------------------------------------------------------------------------
static __inline__ uint16_t htole16(uint16_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return (uint16_t) ((u >> 8) | ((u & 0xff) << 8));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_HTOLE32
//---------------------------------------------------------------------------
static __inline__ uint32_t htole32(uint32_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return (u << 24) | ((u << 8) & 0xff0000) | ((u >> 8) & 0xff00) | (u >> 24);
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_HTOLE64
//---------------------------------------------------------------------------
static __inline__ uint64_t htole64(uint64_t u)
{
#if BYTE_ORDER == LITTLE_ENDIAN
  return u;
#else
  return ((uint64_t) be32toh((uint32_t) u) << 32) | be32toh((uint32_t) (u >> 32));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE16ENC
//---------------------------------------------------------------------------
static __inline__ void be16enc(void * pp,uint16_t u)
{
#if HAVE_HTOBE16
  *(uint16_t *) pp = htobe16(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t * p = (uint8_t *) pp;

  p[0] = (uint8_t) ((u >> 8) & 0xff);
  p[1] = (uint8_t) (u & 0xff);
#else
  *(uint16_t *) pp = u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE32ENC
//---------------------------------------------------------------------------
static __inline__ void be32enc(void * pp,uint32_t u)
{
#if HAVE_HTOBE32
  *(uint32_t *) pp = htobe32(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t * p = (uint8_t *) pp;

  p[0] = (uint8_t) ((u >> 24) & 0xff);
  p[1] = (uint8_t) ((u >> 16) & 0xff);
  p[2] = (uint8_t) ((u >> 8) & 0xff);
  p[3] = (uint8_t) (u & 0xff);
#else
  *(uint32_t *) pp = u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE64ENC
//---------------------------------------------------------------------------
static __inline__ void be64enc(void * pp,uint64_t u)
{
#if HAVE_HTOBE64
  *(uint64_t *) pp = htobe64(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t *p = (uint8_t *) pp;

  be32enc(p,(uint32_t) (u >> 32));
  be32enc(p + 4,(uint32_t) (u & 0xffffffff));
#else
  *(uint64_t *) pp = u;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE16ENC
//---------------------------------------------------------------------------
static __inline__ void le16enc(void *pp,uint16_t u)
{
#if HAVE_HTOLE16
  *(uint16_t *) pp = htole16(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  *(uint16_t *) pp = u;
#else
  uint8_t * p = (uint8_t *) pp;

  p[0] = (uint8_t) ((u >> 8) & 0xff);
  p[1] = (uint8_t) (u & 0xff);
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE32ENC
//---------------------------------------------------------------------------
static __inline__ void le32enc(void *pp, uint32_t u)
{
#if HAVE_HTOLE32
  *(uint32_t *) pp = htole32(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  *(uint32_t *) pp = u;
#else
  uint8_t * p = (uint8_t *) pp;

  p[0] = (uint8_t) ((u >> 24) & 0xff);
  p[1] = (uint8_t) ((u >> 16) & 0xff);
  p[2] = (uint8_t) ((u >> 8) & 0xff);
  p[3] = (uint8_t) (u & 0xff);
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE64ENC
//---------------------------------------------------------------------------
static __inline__ void le64enc(void *pp, uint64_t u)
{
#if HAVE_HTOLE64
  *(uint64_t *) pp = htole64(u);
#elif BYTE_ORDER == LITTLE_ENDIAN
  *(uint64_t *) pp = u;
#else
  uint8_t * p = (uint8_t *) pp;

  le32enc(p,(uint32_t) (u >> 32));
  le32enc(p + 4,(uint32_t) (u & 0xffffffff));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE16DEC
//---------------------------------------------------------------------------
static __inline__ uint16_t be16dec(const void * pp)
{
#if HAVE_BE16TOH
  return be16toh(*(uint16_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t const * p = (uint8_t const *) pp;
  return (uint16_t) (((p[0] << 8) | p[1]));
#else
  return *(uint16_t *) pp;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE32DEC
//---------------------------------------------------------------------------
static __inline__ uint32_t be32dec(const void * pp)
{
#if HAVE_BE32TOH
  return be32toh(*(uint32_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t const * p = (uint8_t const *) pp;
  return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
#else
  return *(uint32_t *) pp;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_BE64DEC
//---------------------------------------------------------------------------
static __inline__ uint64_t be64dec(const void *pp)
{
#if HAVE_BE64TOH
  return be64toh(*(uint64_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  uint8_t const * p = (uint8_t const *)pp;
  return (((uint64_t) be32dec(p) << 32) | be32dec(p + 4));
#else
  return *(uint64_t *) pp;
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE16DEC
//---------------------------------------------------------------------------
static __inline__ uint16_t le16dec(const void * pp)
{
#if HAVE_LE16TOH
  return le16toh(*(uint16_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  return *(uint16_t *) pp;
#else
  return (uint16_t) ((u >> 8) | ((u & 0xff) << 8));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE32DEC
//---------------------------------------------------------------------------
static __inline__ uint32_t le32dec(const void *pp)
{
#if HAVE_LE32TOH
  return le32toh(*(uint32_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  return *(uint32_t *) pp;
#else
  return (u << 24) | ((u << 8) & 0xff0000) | ((u >> 8) & 0xff00) | (u >> 24);
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_LE64DEC
//---------------------------------------------------------------------------
static __inline__ uint64_t le64dec(const void *pp)
{
#if HAVE_LE64TOH
  return le64toh(*(uint64_t *) pp);
#elif BYTE_ORDER == LITTLE_ENDIAN
  return *(uint64_t *) pp;
#else
  return ((uint64_t) be32toh((uint32_t) u) << 32) | be32toh((uint32_t) (u >> 32));
#endif
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_GETTIMEOFDAY
#if _MSC_VER
struct timezone
{
  int tz_minuteswest; /* of Greenwich */
  int tz_dsttime;     /* type of dst correction to apply */
};
#endif
int gettimeofday(struct timeval * tvp,struct timezone * tzp);
#endif
#if !HAVE_TIMEGM
time_t timegm(struct tm * t);
#endif
int64_t get_time(void);
int64_t get_local_time(void);
int64_t getgmtoffset(void);
void time2tm(struct tm * t,int64_t a);
void get_time_code_string(string * s,int64_t t);
void get_time_string(string * s,int64_t t);
//---------------------------------------------------------------------------
static __inline__ void time2timeval(struct timeval * tv,int64_t a)
{
  tv->tv_sec = a / 1000000u;
  tv->tv_usec = a % 1000000u;
}
//---------------------------------------------------------------------------
static __inline__ int64_t timeval2time(const struct timeval * tv)
{
  return tv->tv_sec * UINT64_C(1000000) + tv->tv_usec;
}
//---------------------------------------------------------------------------
static __inline__ int64_t tm2time(struct tm * t)
{
  return timegm(t) * UINT64_C(1000000);
}
//---------------------------------------------------------------------------
static __inline__ void tm2timeval(struct timeval * tv,struct tm * t)
{
  tv->tv_sec = timegm(t);
  tv->tv_usec = 0;
}
//---------------------------------------------------------------------------
#ifdef __cplusplus
};
#endif
//---------------------------------------------------------------------------
#endif // __sys_h__
//---------------------------------------------------------------------------
